<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <script>
        /**
         * 类的声明
         */
        function Animal() {
            this.name = 'name';
        }
        /**
         * ES6中classs声明
         */
        class Animal2 {
            constructor() {
                this.name = 'name';
            }
        }
        /**
         * 实例化
         */
        console.log(new Animal(), new Animal2())

        /**
         * 借助构造函数实现继承（实现了部分继承）
         * 原理：改变了Parent1中的this指向，
         * 缺点：Parent1原型链上的方法并没有被Child1继承
         */
        function Parent1() {
            this.name = 'Parent1';
        }
        Parent1.prototype.say = function() {};
        function Child1() {
            Parent1.call(this); // apply 改变this指向
            this.type = 'Child1';
        }
        var s1 = new Child1();
        console.log(s1); // Child1 {name: "Parent1", type: "Child1"}
        console.log(s1.constructor === Parent1) // true

        /**
         * 借助原型链实现继承
         * 缺点：假如实例化两个对象，改变其中一个，另一个也会跟着变化
         * 原因：因为原型链中的原型对象是共用的，s2_1.__proto__ = s2_2.__proto__
         */
        function Parent2() {
            this.name = 'Parent2';
            this.play = [1, 2, 3];
        }
        function Child2() {
            this.type = 'Child2';
        }
        Child2.prototype = new Parent2();
        
        var s2_1 = new Child2();
        var s2_2 = new Child2();
        s2_1.play.push(4);
        console.log('s2_1(push 4):', s2_1.play); // s2_1(push 4): [1, 2, 3, 4]
        console.log('s2_2:', s2_2.play); // s2_1: [1, 2, 3, 4]
        console.log(s2_1.__proto__ === Child2.prototype, Child2.prototype.__proto__ === Parent2.prototype); // true true
        console.log(s2_1.constructor === Parent2); // true
        console.log(s2_1.__proto__ === s2_2.__proto__); // true

        /**
         * 组合方式
         * 缺点：① 实例化子类时，父类执行了两次（new时、原型链上）
         *      ② 对象是由父类实例化的
         */
         function Parent3() {
            this.name = 'Parent3';
            this.play = [1, 2, 3];
        }
        function Child3() {
            Parent3.call(this); // apply 改变this指向
            this.type = 'Child3';
        }
        Child3.prototype = new Parent3();
        var s3_1 = new Child3();
        var s3_2 = new Child3();
        s3_1.play.push(4);
        console.log('s3_1(push 4):', s3_1.play); // s3_1(push 4): [1, 2, 3, 4]
        console.log('s3_2:', s3_2.play); // s3_1: [1, 2, 3]

        /**
         * 组合继承优化方式1
         * 缺点：对象是由父类实例化的
         */
         function Parent4() {
            this.name = 'Parent4';
            this.play = [1, 2, 3];
        }
        function Child4() {
            Parent4.call(this); // apply 改变this指向
            this.type = 'Child4';
        }
        Child4.prototype = Parent4.prototype;
        var s4 = new Child4();
        s4.play.push(4);
        console.log('s4:', s4 instanceof Child4, s4 instanceof Parent4);
        // 怎么区分一个对象是父类实例化的，还是子类实例化的
        // 使用 constructor属性
        console.log(s4.constructor == Parent4);

        /**
         * 组合继承优化方式2
         * Object.create(Parent5.prototype) 创建的对象 原型对象是参数
         */
         function Parent5() {
            this.name = 'Parent5';
            this.play = [1, 2, 3];
        }
        function Child5() {
            Parent5.call(this); // apply 改变this指向
            this.type = 'Child5';
        }
        Child5.prototype = Object.create(Parent5.prototype);
        Child5.prototype.constructor = Child5;
        var s5 = new Child5();
        console.log('s5:', s5 instanceof Child5); // true true
        // 怎么区分一个对象是父类实例化的，还是子类实例化的
        // 使用 constructor属性
        console.log(s5.constructor == Parent5); // false
        console.log(s5);  // Child5 {name: "Parent5", play: Array(3), type: "Child5"}
    </script>
</body>
</html>